from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager

import lxml.html
from lxml import etree
from bs4 import BeautifulSoup

from textblob import TextBlob
from pathlib import Path
from loguru import logger

import time
import re
import csv
import random

from database import DataBase
from browser import Browser



class Parser(Browser):

    """ Класс представляющий парсер """

    def __init__(self):

        self.home_page = 'https://stackoverflow.com/questions/'
        self.browser = self.get_browser()
        self.launch_browser(url=self.home_page)
        self.parsing_process()


    def convert_to_bs(self):

        """ Получить объект BeautifulsSoup из Selenium """

        html_source = self.browser.page_source
        page = BeautifulSoup(html_source, 'lxml')

        return page


    def get_bs_object(self, file_name):

        """ Открыть сохранненый файл страницы и вернуть его в виде объекта bs """

        page_file = Path('stackoverflow_html/')
        page_file = page_file/file_name

        with open(page_file, 'r', encoding='utf-8') as file:

            text = file.read()
            page = BeautifulSoup(text, 'lxml')

            return page


    def wait(self, t=2.6):

        """ Чуть-чуть подождать """

        t = random.uniform(1, t)

        print(f'Ждем {t}\n')
        time.sleep(t)


    def write_to_csv(self, data):

        """ Записать переданные данные в csv файл """

        with open('data.csv', 'a') as file:

            writer = csv.writer(file)

    def close_banners(self):

        """ Закрываем баннеры """

        self.browser.find_element(
            By.XPATH,
            '//div[@class="ff-sans ps-fixed z-nav-fixed ws4 sm:w-auto p32 sm:p16 bg-black-750 fc-white bar-lg b16 l16 r16 js-consent-banner"]//div[@class="grid gs8 ai-stretch fd-column sm:fd-row"]/button[1]'
        ).click()

        self.browser.implicitly_wait(5)


    def parsing_process(self):

        """ Весь процесс парсинга """

        def go_to_next_page(page_number):

            """ Перейти на следующую страницу вопросов """

            try:

                link = f'https://stackoverflow.com/questions?tab=newest&page={page_number}'
                self.launch_browser(url=link)

            except:

                return 0


        page_number = 1

        while True:

            links_all_questions = self.get_links_all_questions() # Получаем список вопросов со страницы

            for i, question_link in enumerate(links_all_questions, 1):

                print(f'Вопрос #{i} страницы {page_number}')
                print(f'Его ссылка - {question_link}')

                try:

                    self.launch_browser(url=question_link) # Открываем первый вопрос

                except:

                    print('С этим вопросом что-то не то, смотри логи')

                    logger.error('logging_/ERROR.log', format='{time} {level} {message}',
                                 level='ERROR', rotation='1 week', compression='zip')

                    continue

                well_what = self.get_content_question()

                if not well_what:

                    print('У этого вопроса нет ответов')

                self.wait()

            page_number += 1

            try:

                go_to_next_page(page_number) # Переходим на следующую страницу

            except:

                print('Такой страницы нет')
                break


    def get_links_all_questions(self):

        """ Получить ссылки на все вопросы """

        links = []
        temp = ['https://stackoverflow.com/questions/21659691/error-1452-cannot-add-or-update-a-child-row-a-foreign-key-constraint-fails']

        question_links = self.browser.find_elements(
            By.XPATH,
            '//div[@id="questions"]/div[@class="question-summary"]//h3/a'
        )

        for question_link in question_links:

            address = question_link.get_attribute('href')

            links.append(address)


        return temp


    def translate_text(self, text):

        """ Переводим полученный текст и возвращаем его """

        def cut_out_excess(text):

            """ Вырезать лишние теги, которые не требуются в вопросе """

            result = re.sub(r'<aside.*?>.*?</aside>', '', str(text), flags=re.S)

            return result


        def cut_code_from_question(text):

            """ Получаем все блоки кода из вопроса и возврощаем их """

            cut_out_code = re.findall(r'<pre.+?>.+?</pre>|<hr/>|<a.+?>.+?</a>|<code.*?>.*?</code>|<blockquote.*?>.*?</blockquote>', text, flags=re.S)

            return cut_out_code


        def get_translated_text(text):

            """ Переводим текст """

            blob = TextBlob(text)
            trans = blob.translate(to='ru')

            return trans


        def remove_unnecessary_parts(text):

            """ Удаляем части текста, переводить которые не нужно.
            Возвращаем готовый текст для перевода """

            cleared_text = re.sub(r'<pre.+?>.+?</pre>|<hr/>|<a.+?>.+?</a>|<code.*?>.*?</code>|<blockquote.*?>.*?</blockquote>', 'Ё', str(text), flags=re.S)

            return cleared_text


        def insert_code_in_text(text):

            """ Вставляем код в переведенный текст """

            done_text = []
            count = 0
            text = text.split('Ё')

            for part in text:

                if count == len(cut_out_code):

                    if len(text) != len(cut_out_code):

                        done_text.append(text[-1])

                    result = ''.join(done_text)

                    return result

                done_text.append(part + cut_out_code[count])
                count += 1


        text = cut_out_excess(text)                          # Очищенный текст
        cut_out_code = cut_code_from_question(text)          # Блоки кода из вопроса
        cleared_text = remove_unnecessary_parts(text)        # Получаем 'чистый' текст для перевода
        translated_text = get_translated_text(cleared_text)  # Получаем переведенный текст
        done = insert_code_in_text(translated_text)          # Готовый вопрос

        return done


    def get_content_question(self):

        """ Собираем информацию со всего вопроса """

        def get_question_title():

            """ Получить заголовок вопроса """

            question_title = content.find('div', {'id': 'question-header'}) \
                .find('h1').find('a').text
            result = self.translate_text(question_title)

            return result


        def get_question_link():

            """ Получить ссылку вопроса """

            question_link = content.find('div', {'id': 'question-header'}) \
                .find('h1').find('a')['href']
            result = 'https://stackoverflow.com' + question_link

            return result


        def get_activity_information():

            """ Получить значения об активности вопроса """

            activity = page.find('div', {'class': 'inner-content'})

            # Вопрос задан
            question_asked = activity.find('time', {'itemprop': 'dateCreated'}).text

            # Последняя активность
            last_activity = activity.find('a', {'class': 's-link'}).text

            # Количество просмотров
            viewed = activity.find_all('span', {'class': 'fc-light'})[2] \
                .parent.text
            viewed = re.findall(r'\d+', viewed)[0]

            data = (question_asked, last_activity, viewed)

            return data


        def get_upvote_count():

            """ Получить количество голосов вопроса """

            upvote_count = full_question.find('div', {'itemprop': 'upvoteCount'}).text

            return upvote_count


        def get_question_content():

            """ Получить основное содержание вопроса """

            content = full_question.find('div', {'class': 'postcell'}) \
                .find('div', {'class': 'js-post-body'})

            translated_text = self.translate_text(content) # Переведенное содержание вопроса

            return translated_text


        def get_question_tags():

            """ Получить все теги вопроса """

            print('Смотрим теги вопроса')

            def get_description_tags(tag):

                """ Получить описание тега """

                link = f'https://stackoverflow.com/questions/tagged/{tag}'
                self.launch_browser(link)

                tag_description = self.browser.find_element(
                    By.XPATH,
                    '//div[@id="mainbar"]//p'
                ).text

                result = self.translate_text(tag_description)

                return result


            done_list_tags = []
            tags = full_question.find_all('a', {'class': 'post-tag'})

            for tag in tags:

                tag_name = tag.text
                tag_desc = get_description_tags(tag_name)
                done_list_tags.append([tag_name, tag_desc])

            return done_list_tags


        def get_related_questions():

            """ Получить похожие вопросы """

            result = []

            q_related = page.find('div', {'class': 'sidebar-related'}) \
                .find_all('div', {'class': 'spacer'})

            for q in q_related:

                vote_related_q = q.find_all('a')[0].text.strip()
                text_related_q = q.find_all('a')[1].text
                translated_text_q = self.translate_text(text_related_q) # Переводим текст
                href_related_q = q.find_all('a')[1]['href']

                result.append((vote_related_q, translated_text_q, href_related_q))

            return result


        def get_question_comments():

            """ Получить комментарии к вопросу """

            try:

                # Закрываем все возможные банеры

                #self.launch_browser(url='https://stackoverflow.com/questions/17778372/why-does-my-recursive-function-return-none')

                self.browser.find_element(
                    By.XPATH,
                    '/html/body/div[5]/div/button[1]'
                ).click()

                self.browser.implicitly_wait(5)

                self.browser.find_element(
                    By.XPATH,
                    '//*[@id="openid-buttons"]/button[4]'


                ).click()

                self.browser.implicitly_wait(5)

                # Нажимаем кнопку показать больше
                btn = self.browser.find_element(
                    By.XPATH,
                    '//div[@class="question"]//a[@class="js-show-link comments-link "]'
                )
                btn.click()

                time.sleep(2.5)

            except Exception as ex:

                # Если баннеров или вопросов больше нет - ничего не делаем
                pass

            result = []
            comments = page.find('div', {'class': 'js-post-comments-component'}) \
                .find('ul', {'class': 'comments-list'}) \
                .find_all('li')


            for comment in comments:

                c_actions = comment.find('div', {'class': 'comment-actions'}).text.strip()
                c_text = comment.find('div', {'class': 'js-comment-text-and-form'}) \
                    .text.strip()
                c_text = re.split(r'\w{3}\s\d+\s\'\d+\sat\s', c_text)[0]

                c_text_done = self.translate_text(c_text)

                result.append([c_actions, c_text_done])


            return result


        def get_question_answer():

            """ Получить все ответы к вопросу """

            def get_answer_votes(answer):

                """ Получить количество голосов ответа """

                number_votes = answer.find('div', {'class': 'votecell'}) \
                    .find('div', {'class': 'js-vote-count'}).text.strip()

                return number_votes


            def get_text_answer(answer):

                """ Получить текст ответа перевести его """

                text = answer.find('div', {'class': 'answercell'}) \
                    .find('div', {'class': 'js-post-body'})

                translated_text = self.translate_text(text) # Получаем переведенный текст

                return translated_text


            def get_content_answer(all_answers, answer_from_page):

                """ Получаем содержимое ответа """

                for i, answer in enumerate(answer_from_page, 1):

                    print(f'Ответ №{i}')

                    number_votes = get_answer_votes(answer)
                    text_answer = get_text_answer(answer)

                    all_answers.append([number_votes, text_answer])


            def get_number_pages_with_answers():

                """ Проверяем есть страницы с ответами еще """

                result = []

                try:

                    pagination = self.browser.find_elements(
                        By.XPATH,
                        '//div[@class="s-pagination pager-answers"][1]/*'
                    )

                except: # Значит страниц, с дополнительными ответами нет

                    return 0

                number_elements = len(pagination) - 1

                for n in range(1, number_elements + 1):

                    link = f'https://stackoverflow.com/questions/11/calculate-relative-time-in-c-sharp?page={n}&tab=votes#tab-top'
                    result.append(link)

                return result[1:]


            print('Смотрим ответы к вопросу')
            all_answers = []

            page = self.convert_to_bs()
            answer_from_page = page.find('div', {'id': 'answers'}) \
                .find_all('div', {'class': 'answer'}) # Все ответы с вопроса

            if answer_from_page == 0:

                return 0

            get_content_answer(all_answers, answer_from_page) # Достаем все из вопроса
            additional_p = get_number_pages_with_answers() # Узнаем, есть ли еще страницы с вопросами

            if additional_p: # Если есть дополнительные страницы с ответами

                for p in additional_p:

                    self.launch_browser(p)
                    page = self.convert_to_bs()

                    answer_from_page = page.find('div', {'id': 'answers'}) \
                        .find_all('div', {'class': 'answer'}) # Все ответы с вопроса
                    get_content_answer(all_answers, answer_from_page) # Достаем все из вопроса

            return all_answers

        # НИЖЕ ВЫЗОВ ВСЕХ ФУНКЦИЙ

        page = self.convert_to_bs()
        content = page.find('div', {'id': 'content'})
        full_question = content.find('div', {'id': 'mainbar'}) \
            .find('div', {'class': 'question'})

        q_answer = get_question_answer()

        if not q_answer: # Если нет ответов, то и вопрос этот не нужен

            return


        self.browser.implicitly_wait(5)

        q_title = get_question_title()
        q_link = get_question_link()
        q_activity = get_activity_information()
        q_upvote_count = get_upvote_count()
        q_content = get_question_content()
        q_tags = get_question_tags()
        q_related = get_related_questions()
        q_comments = get_question_comments()


        db = DataBase(
            title=q_title,
            link=q_link,
            upvote=q_upvote_count,
            related_questions=q_related,
            answers=q_answer,
            tags=q_tags,
            content=q_content,
            comments=q_comments,
        )



a = Parser()
